import { Component, ChangeDetectionStrategy, ViewChild, Input, ChangeDetectorRef, ViewContainerRef, ComponentRef, OnDestroy, ComponentFactoryResolver, Output, EventEmitter } from '@angular/core';
import { MdcDrawer } from '@angular-mdc/web';
import { map, merge } from 'ramda';
import { v4 } from 'uuid';
import { first, takeUntil } from 'rxjs/operators';
import { ReplaySubject, Subject } from 'rxjs';
import { Control, DashboardType, ControlType } from '../../dashboard/dashboard-model';
import { configureControls, defaultControls, filterUnnecessaryConfig } from '../../dashboard/utils';

import { HeaderPanelComponent } from '../builder-config-panels/header-panel/header-panel.component';
import { TextPanelComponent } from '../builder-config-panels/text-panel/text-panel.component';
import { TextAreaPanelComponent } from '../builder-config-panels/text-area-panel/text-area-panel.component';
import { RadioPanelComponent } from '../builder-config-panels/radio-panel/radio-panel.component';
import { CheckboxPanelComponent } from '../builder-config-panels/checkbox-panel/checkbox-panel.component';
import { SwitchPanelComponent } from '../builder-config-panels/switch-panel/switch-panel.component';
import { DatetimePanelComponent } from '../builder-config-panels/datetime-panel/datetime-panel.component';
import { BasePanel } from '../builder-config-panels/base-panel/base-panel';

@Component({
  selector: 'app-builder-main',
  templateUrl: './builder-main.component.html',
  styleUrls: ['./builder-main.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BuilderMainComponent implements OnDestroy {
  @Output() builderChange = new EventEmitter<Control[]>();

  destroy$ = new Subject();

  @ViewChild('configPanel', { read: ViewContainerRef })
  content: ViewContainerRef;

  componentRef: ComponentRef<any>;

  @ViewChild(MdcDrawer)
  drawer: MdcDrawer;

  @Input() set items(items: Control[]) {
    if (!items) {
      return;
    }

    this._items = configureControls(items);
  }

  _items: Control[];

  formControls: Control[] = configureControls(defaultControls);

  dragging: Control;

  configured: Control;

  completeConfigured: Control;

  completeConfigured$ = new ReplaySubject<Control>(1);

  dashboardType = DashboardType.Builder;

  constructor(
    public changeDetectorRef: ChangeDetectorRef,
    public componentFactory: ComponentFactoryResolver,
  ) { }

  ngOnDestroy() {
    if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = undefined;
    }

    this.destroy$.next();
    this.destroy$.complete();
  }

  itemsChange(event: Control[]) {
    this._items = event;

    this.builderChange.emit(filterUnnecessaryConfig(this._items));
  }

  openDrawer() {
    if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = undefined;
    }

    this.createConfigurePanel(this.configured);

    if (this.drawer.open) {
      return;
    }

    this.drawer.open = true;
  }

  closeDrawer() {
    if (!this.drawer.open) {
      return;
    }

    this.drawer.open = false;
  }

  createConfigurePanel(control: Control) {
    let component: any;

    switch (control.type) {
      case ControlType.Header:
        component = HeaderPanelComponent;
        break;
      case ControlType.Text:
        component = TextPanelComponent;
        break;
      case ControlType.Textarea:
        component = TextAreaPanelComponent;
        break;
      case ControlType.Radio:
        component = RadioPanelComponent;
        break;
      case ControlType.Checkbox:
        component = CheckboxPanelComponent;
        break;
      case ControlType.Switch:
        component = SwitchPanelComponent;
        break;
      case ControlType.Datetime:
        component = DatetimePanelComponent;
        break;
      default:
        break;
    }
    
    const factory = this.componentFactory.resolveComponentFactory(component);
    this.componentRef = this.content.createComponent(factory);
    
    let instance = <BasePanel>this.componentRef.instance;
    instance.control = this.configured;

    instance.changeControl.pipe(
      takeUntil(this.destroy$),
    ).subscribe((control: Control) => {
      this.completeConfigured$.next(control);
    });
  }

  saveControlChanges() {
    this.completeConfigured$.pipe(
      first(),
    ).subscribe(control => {
      this._items = map(item => {
        if (item.uuid === control.uuid) {
          return merge(item, control);
        }
  
        return item;
      }, this._items);

      this.builderChange.emit(filterUnnecessaryConfig(this._items));
      
      this.completeConfigured = control;
      this.changeDetectorRef.markForCheck();
  
      this.closeDrawer();
    });
  }

  dragStart(event: DragEvent, control: Control) {
    event.dataTransfer.dropEffect = 'copy';

    this.dragging = merge(control, { uuid: v4() });
  }

}